﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Net;
using System.Xml;
using System.Web.Services.Protocols;

using TFSAdministrationTool.Proxy.Common;
using TFSAdministrationTool.Proxy.SharePoint;
#endregion

namespace TFSAdministrationTool.Proxy
{
  public static class SharePointProxyFactory
  {
    public delegate ISharePointProxy Factory();
    public static Factory CreateProxy;

    static SharePointProxyFactory()
    {
      CreateProxy = new Factory(CreateRealProxy);
    }

    public static ISharePointProxy CreateSharePointProxy()
    {
      return CreateProxy(); 
    }

    private static ISharePointProxy CreateRealProxy()
    {
      return new SharePointProxy();
    }
  }
  
  public interface ISharePointProxy
  {
    void AddUserToRole(string userName, string role, string displayName, string email, string description);
    void RemoveUserFromRole(string userName, string role);
    void RemoveUser(string userName);

    List<string> GetRoleCollectionFromUser(string userName);
    XmlNode GetRoleCollectionFromWeb();
    SecurityInfo GetSecuritySettings();

    void SetUrl(string url);

    string Url { get; }
    SiteStatus SiteStatus { get; }
    string WssVersion { get; }
  }

  public class SharePointProxy : ISharePointProxy
  {
    #region Fields
    private SharePoint.UserGroup m_UserGroup;
    private SiteStatus m_SiteStatus;
    private string m_WssVersion;
    #endregion

    #region Constructors
    public SharePointProxy()
    {
      m_UserGroup = new UserGroup();
      m_UserGroup.UseDefaultCredentials = true;
    }
    #endregion

    #region Methods
    /// <summary>
    ///   Add a user with a given role to share point
    /// </summary>
    /// <param name="userName">Name of the user</param>
    /// <param name="siteName">SharePoint Site</param>
    /// <param name="role">Name of the role that he is assigned</param>ite
    /// <param name="displayName">Display Name</param>
    /// <param name="email">User's email address</param>
    /// <param name="description">A descripton/comments field about the user</param>
    public void AddUserToRole(string userName, string role, string displayName, string email, string description)
    {
      m_UserGroup.AddUserToRole(role, displayName, userName, email, description);
    }

    /// <summary>
    ///  This method returns a readonly role collection of roles that the user belongs to for a 
    /// given sharepoint site. Since these shouldn't get modified by the caller externally 
    /// they are made readonly.
    /// </summary>
    /// <param name="userName">User Name</param>
    /// <param name="siteName">SharePoint Site Name</param>
    /// <returns>ReadOnlyCollection of string</returns>
    public List<string> GetRoleCollectionFromUser(string userName)
    {
      List<string> spRoles = new List<string>(); ;
      XmlNode result;

      //if (userName == null)
        //throw new InvalidSPUserException(userName, siteName);

      /// Actual call to the SharePoint Web Service to get a list of roles the user belongs to. Its returned
      /// as XML data. 
      try
      {
        result = m_UserGroup.GetRoleCollectionFromUser(userName);
        
        foreach (XmlNode node in result.FirstChild.ChildNodes) {
          spRoles.Add(node.Attributes["Name"].Value);
        }
      }
      catch (SoapException ex)
      {
        TfsAdminToolTracer.TraceException(TfsAdminToolTracer.TraceSwitch.TraceError, ex);
      }

      return spRoles;
    }

    public XmlNode GetRoleCollectionFromWeb()
    {
      return m_UserGroup.GetRoleCollectionFromWeb();
    }

    /// <summary>
    /// Get the security settings of the site and formats them into an XmlNode.
    /// It also detects if the site for the TeamProject/Server exists. We do this
    /// so that no additional calls are made to the server in order to detect site
    /// existence.
    /// </summary>
    /// <returns></returns>
    public SecurityInfo GetSecuritySettings()
    {
      XmlNode node = null;
      SecurityInfo spSecurityInfo = new SecurityInfo();

      // Try to get the roles for the site. If this call results in a 
      // WebException with HttpStatusCode 404 than the site does not exist
      try
      {
        node = GetRoleCollectionFromWeb();
      }
      catch (WebException ex)
      {
        TfsAdminToolTracer.TraceException(TfsAdminToolTracer.TraceSwitch.TraceError, ex);

        switch (((HttpWebResponse)ex.Response).StatusCode)
        {
          case HttpStatusCode.NotFound:
            m_SiteStatus = SiteStatus.Unavailable;
            break;
          case HttpStatusCode.Forbidden:
          case HttpStatusCode.Unauthorized:
            m_SiteStatus = SiteStatus.Unauthorized;
            break;
          default:
            m_SiteStatus = SiteStatus.Error;
            break;
        }

        return spSecurityInfo;
      }
      catch (Exception ex)
      {
        TfsAdminToolTracer.TraceException(TfsAdminToolTracer.TraceSwitch.TraceError, ex);
        m_SiteStatus = SiteStatus.Error;
        return spSecurityInfo;
      }
      
      // If we managed to get the roles, loop through them
      foreach (XmlNode roleNode in node.ChildNodes[0].ChildNodes)
      {
        if (String.Compare(roleNode.Attributes["Hidden"].Value, "False", true) == 0)
        {
          SecurityGroup sGroup = new SecurityGroup() { DisplayName = roleNode.Attributes["Name"].Value };
          XmlNode usersNode = m_UserGroup.GetUserCollectionFromRole(roleNode.Attributes["Name"].Value);
          
          foreach (XmlNode userNode in usersNode.FirstChild.ChildNodes)
          {
            sGroup.AddUser(userNode.Attributes["Sid"].Value, userNode.Attributes["Name"].Value, userNode.Attributes["Name"].Value); 
          }
          spSecurityInfo.AddGroup(sGroup);
        }
      }

      m_SiteStatus = SiteStatus.Available;
      
      return spSecurityInfo;
    }

    public void RemoveUser(string userName)
    {
      List<string> assignedRoles = GetRoleCollectionFromUser(userName);

      foreach (string assignedRole in assignedRoles)
      {
        m_UserGroup.RemoveUserFromRole(assignedRole, userName);
      }
    }

    /// <summary>
    ///  Remove the user from a given role assignment on the Share Point site.
    /// </summary>
    /// <param name="userName">User Name</param>
    /// <param name="siteName">SharePoint Site Name</param>
    /// <param name="role">Role Name</param>
    public void RemoveUserFromRole(string userName, string role)
    {
      m_UserGroup.RemoveUserFromRole(role, userName);
    }

    public void SetUrl(string url)
    {
      m_UserGroup.Url = url;
      m_WssVersion = GetVersion();
    }

    private string GetVersion()
    {
      WebRequest request = WebRequest.Create(m_UserGroup.Url);
      request.UseDefaultCredentials = true;
      WebResponse response = request.GetResponse();

      try
      {
        if (response.Headers.Get("MicrosoftSharePointTeamServices").StartsWith("12"))
        {
          return "12";
        }
        else
        {
          return "6";
        }
      }
      catch (NotSupportedException ex)
      {
        TfsAdminToolTracer.TraceException(TfsAdminToolTracer.TraceSwitch.TraceError, ex);
        return "6";
      }
    }
    #endregion

    #region Properties

    public string Url
    {
      get
      {
        return m_UserGroup.Url;
      }
    }
    
    public SiteStatus SiteStatus
    {
      get
      {
        return m_SiteStatus;
      }
    }

    public string WssVersion
    {
      get
      {
        return m_WssVersion;
      }
    }
    #endregion
  } //End Class
} //End Namespace